package com.ht.module.file.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ht.abnormal.HtException;
import com.ht.config.dataAuth.DataScope;
import com.ht.config.minio.MinioConfig;
import com.ht.config.minio.service.MinioService;
import com.ht.config.onlyoffcie.properties.OnlyProperties;
import com.ht.constant.BusConstant;
import com.ht.constant.FileConstant;
import com.ht.module.file.dto.OperationFileDto;
import com.ht.module.file.entity.FileInfo;
import com.ht.module.file.mapper.FileInfoMapper;
import com.ht.module.file.service.FileInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ht.module.file.service.OnlyOfficeService;
import com.ht.util.CommMethod;
import com.ht.util.FileUtil;
import com.ht.util.Pager;
import com.ht.util.UserUtil;
import com.ht.util.ssh.SshConnection;
import com.ht.util.ssh.SshResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import java.io.*;
import java.math.BigDecimal;

import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * <p>
 * 文件信息表 服务实现类
 * </p>
 *
 * @author hejialun
 * @since 2024-06-12
 */
@Service
@Slf4j
public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileInfoService {
    @Resource
    private MinioService minioService;

    @Resource
    private HttpServletResponse response;
    @Resource
    private OnlyOfficeService onlyOfficeService;
    @Resource
    private OnlyProperties onlyProperties;


    @Override
    public Pager<FileInfo> findPage(Pager<FileInfo> pager, FileInfo en) {
        QueryWrapper<FileInfo> qw = new QueryWrapper<>();
        if (StrUtil.isNotEmpty(en.getFileName())) {
            qw.like("file_name", en.getFileName());
        }
        if (StrUtil.isNotEmpty(en.getFileType())) {
            qw.eq("file_type", en.getFileType());
        }
        return baseMapper.selectPage(pager, qw);
    }

    @Override
    public FileInfo uploadFile(MultipartFile file, FileInfo en) {

        if (BeanUtil.isEmpty(file)) {
            throw new HtException(FileConstant.FILE_NO_EXIST);
        }
        //定义文件实体
        FileInfo fileInfo = new FileInfo();
        CommMethod.beanCreate(fileInfo);
        //设置文件名称
        fileInfo.setFileName(file.getOriginalFilename());
        //设置文件大小
        fileInfo.setFileSize(new BigDecimal(file.getSize() / 1024));
        //获取文件类型
        fileInfo.setFileType(FileUtil.getFileNameSuffix(file.getOriginalFilename()));

        //限制单位文件大小不能超过10M
        if (file.getSize() > 1024 * 1024 * 50) {
            throw new HtException("单个文件大小不能超过50M");
        }
        //将当前用户锁住
        //判断当前用户上传文件大小总量-不能大于100M
        List<FileInfo> fileInfos = baseMapper.selectList(new QueryWrapper<FileInfo>().eq("create_user", UserUtil.getUserId()));
        BigDecimal totalSize = new BigDecimal(0);
        for (FileInfo item : fileInfos) {
            totalSize = totalSize.add(item.getFileSize());
        }
        //总量不能大于100M
        if (totalSize.add(fileInfo.getFileSize()).compareTo(new BigDecimal(100 * 1024)) > 0) {
            //计算剩余可以上传的空间
            throw new HtException("用户上传文件大小总量不能超过100M，剩余空间：" + (new BigDecimal(100 * 1024).subtract(totalSize)).intValue() + "KB");
        }

        //设置文件夹
        if (BeanUtil.isEmpty(en) || BeanUtil.isEmpty(en.getFolderId())) {
            fileInfo.setFolderId("0");
        } else {
            fileInfo.setFolderId(en.getFolderId());
        }
        try {
            String filePath = minioService.uploadFile(file);
            fileInfo.setFilePath(filePath);
        } catch (Exception e) {
            e.printStackTrace();
            throw new HtException("文件上传失败！请联系管理员");
        }
        //保存文件
        baseMapper.insert(fileInfo);
        return fileInfo;
    }

    @Override
    public void downloadFileByFileId(String fileId) {
        //查询当前文件
        FileInfo fileInfo = operateFileAuth(fileId);
        try (InputStream inputStream = minioService.downloadFile(fileInfo.getFilePath());) {
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.getFilePath() + "\"");
            IOUtils.copy(inputStream, response.getOutputStream());
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
            throw new HtException(FileConstant.DOWNLOAD_FILE_LOSE);
        }
    }

    @Override
    public void delFileByFileId(String fileId) {
        //判断文件权限
        FileInfo fileInfo = operateFileAuth(fileId);
        //判断是否能查询出当前文件，且拥有权限
        //存在-删除文件
        this.removeById(fileId);
        //删除minio的文件
        try {
            minioService.delByFile(fileInfo.getFilePath());
        } catch (Exception e) {
            log.error("minio文件删除失败：｛｝", e);
        }
    }

    /**
     * 判断是否有操作文件的权限
     *
     * @param fileId
     * @return
     */
    public FileInfo operateFileAuth(String fileId) {
        //判断是否能查询出当前文件，且拥有权限
        //查询是有权限操作当前办件
        List<FileInfo> fileInfos = baseMapper.selectListAuth(
                new QueryWrapper<FileInfo>()
                        .eq("file_id", fileId),
                new DataScope()
        );

        if (fileInfos.isEmpty()) {
            throw new HtException(FileConstant.NO_FILE_AUTH);
        }

        return fileInfos.get(0);
    }

    @Override
    public ModelAndView viewFile(String fileId, Model model) {
        //判断权限
        FileInfo fileInfo = operateFileAuth(fileId);

        String html = FileConstant.FILE_VIEW_WAY.get(fileInfo.getFileType());
        if (StrUtil.isEmpty(html)) {
            //文件类型不支持
            throw new HtException(FileConstant.FILE_NO_SUPPORT);
        }

        //判断文件类型
        if (StrUtil.equals(html, "onlyOffice.html")) {
            return onlyOfficeService.viewBuild(fileInfo, model);
        }

        //判断文件类型
        if (StrUtil.equals(html, "imgView.html")) {
            Map<String, String> map = new HashMap<>();
            map.put("name", fileInfo.getFileName());
            map.put("url", onlyProperties.getDownloadFile() + "/" + fileId);
            model.addAllAttributes(map);
        }

        if (StrUtil.equals(html, "pdfView.html")) {
            Map<String, String> map = new HashMap<>();
            map.put("name", fileInfo.getFileName());
            map.put("url", onlyProperties.getDownloadFile() + "/" + fileId);
            model.addAllAttributes(map);
        }
        return new ModelAndView(html);
    }

    @Override
    public ModelAndView editFile(String fileId, Model model) {
        //判断权限
        FileInfo fileInfo = operateFileAuth(fileId);

        String html = FileConstant.FILE_EDIT_WAY.get(fileInfo.getFileType());
        if (StrUtil.isEmpty(html)) {
            //文件类型不支持
            throw new HtException(FileConstant.FILE_NO_SUPPORT);
        }

        //判断文件类型
        if (StrUtil.equals(html, "onlyOffice.html")) {
            return onlyOfficeService.editBuild(fileInfo, model);
        }

        return new ModelAndView(html);
    }

    private byte[] toByteArray(InputStream inputStream) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        byte[] data = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, bytesRead);
        }
        return buffer.toByteArray();
    }

    @Override
    public void decompression(OperationFileDto en) {
        //判断权限
        FileInfo fileInfo = operateFileAuth(en.getFileId());
        //判断当前文件是否支持解压
        if (!FileConstant.FILE_DECOMPRESSION_WAY.contains(fileInfo.getFileType())) {
            throw new HtException(FileConstant.FILE_NO_DECOMPRESSION);
        }
        try {
            // 从 MinIO 下载 ZIP 文件到 InputStream
            InputStream zipInputStream = minioService.downloadFile(fileInfo.getFilePath());
            ZipArchiveInputStream zipArchiveInputStream = new ZipArchiveInputStream(zipInputStream);
            ZipArchiveEntry entry;
            //定义文件存储集合
            List<FileInfo> fileInfos = new ArrayList<>();
            while ((entry = zipArchiveInputStream.getNextZipEntry()) != null) {
                if (!entry.isDirectory()) {
                    log.info("Extracting file: " + entry.getName());
                    // 读取每个文件内容
                    byte[] fileData = toByteArray(zipArchiveInputStream);
                    try {
                        InputStream fileInputStream = new ByteArrayInputStream(fileData);
                        // 上传解压后的文件到 MinIO
                        String filePath = minioService.uploadFile(fileInputStream, fileData.length, entry.getName());
                        FileInfo temp = new FileInfo()
                                .setFileName(entry.getName())
                                .setFilePath(filePath)
                                .setFileSize(new BigDecimal(entry.getSize() / 1024))
                                .setFileType(FileUtil.getFileNameSuffix(entry.getName()))
                                .setFolderId(en.getFolderId());
                        CommMethod.beanCreate(temp);
                        fileInfos.add(temp);
                    } catch (Exception e) {
                        log.error(FileConstant.FILE_DECOMPRESSION_FAIL, e);
                        throw new HtException(FileConstant.FILE_DECOMPRESSION_FAIL);
                    }
                }
            }
            this.saveBatch(fileInfos);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void viewFileByWindow(String fileId) {
        //判断权限
        FileInfo fileInfo = operateFileAuth(fileId);
        MediaType mediaType = FileConstant.mediaTypeMap.get(fileInfo.getFileType());
        if (BeanUtil.isEmpty(mediaType)) {
            throw new HtException(FileConstant.FILE_NO_SUPPORT);
        }
        try {
            InputStream is = minioService.downloadFile(fileInfo.getFilePath());
            byte[] byteArray = FileUtil.toByteArray(is);
            response.setContentType(mediaType.toString());
            if (byteArray.length > 0) {
                response.getOutputStream().write(byteArray);
                response.getOutputStream().flush();
            } else {
                throw new HtException(FileConstant.DOWNLOAD_FILE_LOSE);
            }
        } catch (Exception e) {
            throw new HtException(FileConstant.DOWNLOAD_FILE_LOSE);
        }

    }

    @Override
    public FileInfo copyFile(OperationFileDto en) {
        //判断权限
        FileInfo fileInfo = operateFileAuth(en.getFileId());

        String filePath = null;
        try {
            filePath = minioService.copyFile(fileInfo.getFilePath());
        } catch (Exception e) {
            e.printStackTrace();
            throw new HtException(FileConstant.FILE_COPY_FAIL);
        }
        //复制文件
        fileInfo.setFolderId(en.getFolderId());
        fileInfo.setFileId(null);
        fileInfo.setFilePath(filePath);
        fileInfo.setFileName(fileInfo.getFileName() + "-copy");
        CommMethod.beanCreate(fileInfo);
        //保存
        baseMapper.insert(fileInfo);
        return fileInfo;
    }

    @Override
    public FileInfo fileToPdf(String fileId) {
        //判断权限
        FileInfo fileInfo = operateFileAuth(fileId);

        //执行文件转pdf
        if (!minioService.fileToPdf(fileInfo.getFilePath())) {
            //执行失败
            throw new HtException("文件转换失败，请联系管理员");
        }

        //获取文件后缀
        String suffix = FileUtil.getFileNameSuffix(fileInfo.getFilePath());
        //获取新文件路径
        String newFilePath = fileInfo.getFilePath().replace("." + suffix, ".pdf");
        fileInfo.setFileName(fileInfo.getFileName() + "-toPDF");
        fileInfo.setFilePath(newFilePath);
        fileInfo.setFileType("pdf");
        fileInfo.setFileId(null);
        CommMethod.beanCreate(fileInfo);
        //新的文件入库
        baseMapper.insert(fileInfo);
        return fileInfo;
    }

    @Override
    public FileInfo moveFile(OperationFileDto en) {
        //判断权限
        FileInfo fileInfo = operateFileAuth(en.getFileId());

        //设置新的文件夹
        fileInfo.setFolderId(en.getFolderId());
        CommMethod.beanUpdate(fileInfo);
        baseMapper.updateById(fileInfo);
        return fileInfo;
    }
}

